#![allow(dead_code)]
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::{PrivateKeyBytes, PublicKeyBytes, PublicKeyHashBytes};

use super::WalletBaseError;

pub struct AccountItemNumber {
    pub account_key_pair_number: usize,
    pub account_name_number: usize,
    pub default_key_number: usize,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct AccountManager {
    key_pair_account: KeyPairAccount,
    pub_key_hash_names: HashMap<PublicKeyHashBytes, String>,
    default_account: Option<PublicKeyHashBytes>,
}

impl Default for AccountManager {
    fn default() -> Self {
        Self::new()
    }
}

impl AccountManager {
    pub fn new() -> Self {
        Self {
            key_pair_account: KeyPairAccount::new(),
            pub_key_hash_names: HashMap::new(),
            default_account: None,
        }
    }

    pub fn get_item_number(&self) -> AccountItemNumber {
        AccountItemNumber {
            account_key_pair_number: self.key_pair_account.get_item_number(),
            account_name_number: self.pub_key_hash_names.len(),
            default_key_number: self.default_account.is_some() as usize,
        }
    }

    pub fn add_key_pair(
        &mut self,
        pub_key: &PublicKeyBytes,
        priv_key: &PrivateKeyBytes,
    ) -> Result<PublicKeyHashBytes, WalletBaseError> {
        self.key_pair_account.add_key_pair(pub_key, priv_key)
    }

    pub fn del_key_pair(&mut self, public_key: &PublicKeyBytes) -> Result<(), WalletBaseError> {
        self.key_pair_account.del_key_pair(public_key)
    }

    pub fn add_account_name(
        &mut self,
        pub_key_hash: &PublicKeyHashBytes,
        name: &str,
    ) -> Result<(), WalletBaseError> {
        if self
            .pub_key_hash_names
            .insert(*pub_key_hash, name.to_string())
            .is_some()
        {
            return Err(WalletBaseError::PubKeyHashAlreadyExists(
                pub_key_hash.to_hex_str(),
            ));
        }
        Ok(())
    }

    pub fn set_default_account(
        &mut self,
        pub_key_hash: PublicKeyHashBytes,
    ) -> Result<(), WalletBaseError> {
        if self.key_pair_account.is_pub_key_hash_exist(&pub_key_hash) {
            self.default_account = Some(pub_key_hash);
            Ok(())
        } else {
            Err(WalletBaseError::PubKeyHashNotExists(pub_key_hash))
        }
    }

    pub fn set_default_account_(
        &mut self,
        pub_key_hash: &PublicKeyHashBytes,
    ) -> Result<(), WalletBaseError> {
        self.default_account = Some(*pub_key_hash);
        Ok(())
    }

    pub fn get_default_account(&self) -> &Option<PublicKeyHashBytes> {
        &self.default_account
    }

    pub fn del_account(&mut self, public_key: &PublicKeyBytes) -> Result<(), WalletBaseError> {
        self.del_key_pair(public_key)?;

        let pub_key_hash = public_key.to_public_key_hash();
        self.pub_key_hash_names.remove(&pub_key_hash);

        if self.default_account == Some(pub_key_hash) {
            self.default_account = None;
        }

        Ok(())
    }

    pub fn get_key_pairs(&self) -> &HashMap<PublicKeyBytes, PrivateKeyBytes> {
        self.key_pair_account.get_key_pairs()
    }

    pub fn get_account_names(&self) -> &HashMap<PublicKeyHashBytes, String> {
        &self.pub_key_hash_names
    }
}

impl AccountManager {
    pub fn get_pub_key(
        &self,
        pub_key_hash: &PublicKeyHashBytes,
    ) -> Result<PublicKeyBytes, WalletBaseError> {
        self.key_pair_account.get_pub_key(pub_key_hash)
    }

    pub fn get_private_key(
        &self,
        public_key: &PublicKeyBytes,
    ) -> Result<PrivateKeyBytes, WalletBaseError> {
        self.key_pair_account.get_private_key(public_key)
    }

    pub fn dump(&self) {
        self.key_pair_account.dump();
    }

    pub fn is_mine(&self, pub_key_hash: &PublicKeyHashBytes) -> bool {
        self.key_pair_account.is_pub_key_hash_exist(pub_key_hash)
    }

    pub fn is_mine_1(&self, public_key: &PublicKeyBytes) -> bool {
        self.key_pair_account.is_pub_key_exist(public_key)
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct KeyPairAccount {
    key_pairs: HashMap<PublicKeyBytes, PrivateKeyBytes>,
    pub_key_hashs: HashMap<PublicKeyHashBytes, PublicKeyBytes>,
}

impl KeyPairAccount {
    fn new() -> Self {
        Self {
            key_pairs: HashMap::new(),
            pub_key_hashs: HashMap::new(),
        }
    }

    fn get_item_number(&self) -> usize {
        let t = self.key_pairs.len();
        assert_eq!(t, self.pub_key_hashs.len());
        t
    }

    fn add_key_pair(
        &mut self,
        pub_key: &PublicKeyBytes,
        priv_key: &PrivateKeyBytes,
    ) -> Result<PublicKeyHashBytes, WalletBaseError> {
        if self
            .key_pairs
            .insert(pub_key.clone(), priv_key.clone())
            .is_some()
        {
            return Err(WalletBaseError::PubKeyAlreadyExists(pub_key.to_hex_str()));
        }

        let pub_key_hash = pub_key.to_public_key_hash();
        if self
            .pub_key_hashs
            .insert(pub_key_hash, pub_key.clone())
            .is_some()
        {
            return Err(WalletBaseError::PubKeyHashAlreadyExists(
                pub_key_hash.to_hex_str(),
            ));
        };

        Ok(pub_key_hash)
    }

    fn del_key_pair(&mut self, public_key: &PublicKeyBytes) -> Result<(), WalletBaseError> {
        if self.key_pairs.remove(public_key).is_none() {
            return Err(WalletBaseError::PubKeyNotExists(public_key.clone()));
        }

        let pub_key_hash = &public_key.to_public_key_hash();
        if self.pub_key_hashs.remove(pub_key_hash).is_none() {
            return Err(WalletBaseError::PubKeyHashNotExists(*pub_key_hash));
        }
        Ok(())
    }

    fn dump(&self) {
        println!("AccountManager:");
        // println!("  key_pairs: {:?}", self.key_pairs);
        let mut index = 0;
        for (public_key, priv_key) in &self.key_pairs {
            println!("[keypair-{}] {} : {}", index, public_key, priv_key);
            index += 1;
        }

        // println!("  pub_keys: {:?}", self.pub_keys);
        index = 0;
        for (pub_key_hash, public_key) in &self.pub_key_hashs {
            println!("[pubkeyhash-{}] {} : {}", index, pub_key_hash, public_key);
            index += 1;
        }
    }

    fn is_pub_key_exist(&self, public_key: &PublicKeyBytes) -> bool {
        self.key_pairs.contains_key(public_key)
    }

    fn is_pub_key_hash_exist(&self, pub_key_hash: &PublicKeyHashBytes) -> bool {
        self.pub_key_hashs.contains_key(pub_key_hash)
    }

    fn get_key_pairs(&self) -> &HashMap<PublicKeyBytes, PrivateKeyBytes> {
        &self.key_pairs
    }

    fn get_pub_key(
        &self,
        pub_key_hash: &PublicKeyHashBytes,
    ) -> Result<PublicKeyBytes, WalletBaseError> {
        if let Some(pub_key) = self.pub_key_hashs.get(pub_key_hash).cloned() {
            Ok(pub_key)
        } else {
            Err(WalletBaseError::PubKeyHashNotExists(*pub_key_hash))
        }
    }

    fn get_private_key(
        &self,
        public_key: &PublicKeyBytes,
    ) -> Result<PrivateKeyBytes, WalletBaseError> {
        if let Some(priv_key) = self.key_pairs.get(public_key) {
            Ok(priv_key.clone())
        } else {
            Err(WalletBaseError::PubKeyNotExists(public_key.clone()))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn prepare_mng() -> (AccountManager, PublicKeyBytes, PrivateKeyBytes) {
        let private_key_bytes = [1; 32];
        let public_key_bytes = [2; 33];
        let priv_key = PrivateKeyBytes::new(private_key_bytes);
        let public_key = PublicKeyBytes::new(public_key_bytes);
        let account_manager = AccountManager::new();
        (account_manager, public_key, priv_key)
    }

    #[test]
    fn test_add_del_key_pair() {
        let (mut account_manager, public_key, priv_key) = prepare_mng();

        let pub_key_hash = account_manager
            .add_key_pair(&public_key, &priv_key)
            .unwrap();

        assert!(account_manager.is_mine_1(&public_key));
        assert!(account_manager.is_mine(&pub_key_hash));
        assert_eq!(
            account_manager.get_pub_key(&pub_key_hash),
            Ok(public_key.clone())
        );
        assert_eq!(
            account_manager.get_private_key(&public_key),
            Ok(priv_key.clone())
        );

        account_manager.del_key_pair(&public_key).unwrap();
        assert!(!account_manager.is_mine_1(&public_key));
        assert!(!account_manager.is_mine(&pub_key_hash));
        assert_eq!(
            account_manager.get_pub_key(&pub_key_hash),
            Err(WalletBaseError::PubKeyHashNotExists(pub_key_hash))
        );
    }

    #[test]
    fn test_del_account() {
        let (mut account_manager, public_key, priv_key) = prepare_mng();

        let pub_key_hash = account_manager
            .add_key_pair(&public_key, &priv_key)
            .unwrap();

        let account_name = "test_account";
        account_manager
            .add_account_name(&pub_key_hash, account_name)
            .unwrap();
        account_manager.set_default_account(pub_key_hash).unwrap();

        assert_eq!(
            account_manager.get_account_names(),
            &HashMap::from([(pub_key_hash, account_name.to_string())])
        );
        assert_eq!(account_manager.get_default_account().unwrap(), pub_key_hash);

        account_manager.del_account(&public_key).unwrap();
        assert_eq!(account_manager.get_account_names(), &HashMap::from([]));
        assert_eq!(*account_manager.get_default_account(), None);
        assert!(!account_manager.is_mine_1(&public_key));
        assert!(!account_manager.is_mine(&pub_key_hash));
        assert_eq!(
            account_manager.get_pub_key(&pub_key_hash),
            Err(WalletBaseError::PubKeyHashNotExists(pub_key_hash))
        );
        assert_eq!(
            account_manager.get_private_key(&public_key),
            Err(WalletBaseError::PubKeyNotExists(public_key))
        );
    }
}
